#include "email_sender.h"

CEmailSender::CEmailSender()
{
	m_SMTPInfo = CAllConfigure::GetInstance().GetSmtpServerConfiguration();
	memset(recvBuffer, 0, 1024);
	
	SendEmailThread = new std::thread(&CEmailSender::threadSendEmail, this);
}

void CEmailSender::threadSendEmail()
{
	while(true)
	{
		if (!q_AllNeedSendEmail.empty())	// nothing to send
		{
			stSendEmailData tempSendEmailData = q_AllNeedSendEmail.front();
			if (!SendEmail(tempSendEmailData.cRecvEmail, tempSendEmailData.cRecvData))
			{
				CEmailSender::GetInstance().initEmailSender();
			}
			else
			{
				mailMutex.lock();
				q_AllNeedSendEmail.pop();
				mailMutex.unlock();
			}
		}

		std::this_thread::sleep_for(std::chrono::seconds(3));
	}
}

CEmailSender::~CEmailSender()
{
	Send(sendEmailLocalSocket, (std::string)"quit\r\n");
	Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer));
	close(sendEmailLocalSocket);

	SendEmailThread->join();
	delete SendEmailThread;
}

bool CEmailSender::initEmailSender()
{

	hostent *stmphost = gethostbyname(m_SMTPInfo.cSmtpServer.c_str());
	if( stmphost == nullptr ) {
		ERROR_MSG("not host: " << m_SMTPInfo.cSmtpServer.c_str());
		return false;
	}

	sockaddr_in smtpAddr;
	memset(&smtpAddr, 0, sizeof(smtpAddr));
	smtpAddr.sin_family = AF_INET;
	smtpAddr.sin_port = htons(m_SMTPInfo.uiPort); //port of SMTP 
	memcpy(&smtpAddr.sin_addr.s_addr,stmphost->h_addr_list[0], stmphost->h_length);
	//connect to the mail server
	sendEmailLocalSocket = socket(PF_INET, SOCK_STREAM, 0);
	if( connect(sendEmailLocalSocket, (sockaddr*)&smtpAddr, sizeof(smtpAddr)) ) 
	{
		ERROR_MSG("failed to connect the smtp mail server");
		return false;
	}

	Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer)); //wait for greeting message
	Send(sendEmailLocalSocket, "HELO " + m_SMTPInfo.cSmtpServer + "\r\n");
	Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer)); //should recv "250 OK"
	Send(sendEmailLocalSocket, (std::string)"auth login\r\n");
	Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer)); //should recv "334 username:"(This is the decode message)
	Send(sendEmailLocalSocket, Base64Encode(m_SMTPInfo.cUsername) + "\r\n");
	Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer));
	if( std::string(recvBuffer).substr(0, 3) != "334" ) {
		ERROR_MSG("username: " << m_SMTPInfo.cUsername.c_str() << "is error !" );
		return false;
	}
	Send(sendEmailLocalSocket, Base64Encode(m_SMTPInfo.cPassword) + "\r\n");
	Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer));
	if( std::string(recvBuffer).substr(0, 3) != "235") {
		ERROR_MSG("password:" << m_SMTPInfo.cPassword.c_str() << " is error!");
		return false;
	}

	INFO_MSG("login email smtp server is successful!");
	return true;
}

bool CEmailSender::Send(int& s, std::string data) {
	if( send(s, data.c_str(), data.length(), 0) == SOCKET_ERROR ) 
	{
		ERROR_MSG("Email Send failed, socket: " << s << " data: " <<  data.c_str());
		return false;
	}

	return true;
}

bool CEmailSender::Recv(int& s, char* buf, int len) {
	memset(buf, 0, len);
	if( recv(s, buf, len, 0) == SOCKET_ERROR ) {
		ERROR_MSG("email while receiving data");
		return false;
	}

	return true;
}

std::string CEmailSender::Base64Encode(const std::string& src) {
	int i, j, srcLen = src.length();
	std::string dst(srcLen / 3 * 4 + 4, 0);
	for(i = 0, j= 0; i <=srcLen - 3; i+=3, j+=4) {
		dst[j] = (src[i] & 0xFC) >> 2;
		dst[j+1] = ((src[i] & 0x03) << 4) + ((src[i+1] & 0xF0) >> 4);
		dst[j+2] = ((src[i+1] & 0x0F) << 2) + ((src[i+2] & 0xC0) >> 6);
		dst[j+3] = src[i+2] & 0x3F;
	}
	if( srcLen % 3 == 1 ) {
		dst[j] = (src[i] & 0xFC) >> 2;
		dst[j+1] = ((src[i] & 0x03) << 4);
		dst[j+2] = 64;
		dst[j+3] = 64;
		j += 4;
	}
	else if( srcLen % 3 == 2 ) {
		dst[j] = (src[i] & 0xFC) >> 2;
		dst[j+1] = ((src[i] & 0x03) << 4) + ((src[i+1] & 0xF0) >> 4);
		dst[j+2] = ((src[i+1] & 0x0F) << 2);
		dst[j+3] = 64;
		j+=4;
	}

	static unsigned char *base64 = (unsigned char*)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
	for(i = 0; i < j; ++i) {    //map 6 bit value to base64 ASCII character
		dst[i] = base64[(int)dst[i]];
	}

	return dst;
}


bool CEmailSender::SendEmail(const std::string& EmailReciver, const std::string& SendData) 
{
	if (!Send(sendEmailLocalSocket, "mail from:<" + m_SMTPInfo.cUsername + ">\r\n")) return false;
	if (!Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer))) return false; //should recv "250 Mail OK"
	if (!Send(sendEmailLocalSocket, "rcpt to:<" + EmailReciver + ">\r\n")) return false;
	if (!Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer))) return false; //should recv "250 Mail OK"
	if (!Send(sendEmailLocalSocket, (std::string)"data\r\n")) return false;
	if (!Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer))) return false; //should recv "354 End data with <CR><LF>.<CR><LF>"
	if (!Send(sendEmailLocalSocket, "to:" + EmailReciver + "\r\n" + "subject:email alert\r\n\r\n" + SendData + "\r\n.\r\n")) return false;
	if (!Recv(sendEmailLocalSocket, recvBuffer, sizeof(recvBuffer))) return false;

	return true;
}

void CEmailSender::PushBackToEmailSendingQueue(const stSendEmailData& emailData)
{
	mailMutex.lock();
	q_AllNeedSendEmail.push(emailData);
	mailMutex.unlock();
}
